Shader "Role/Fur"
{
    Properties
    {
        _BaseColor("Color", Color) = (1, 1, 1, 1)
        _BaseMap("Base Map", 2D) = "white" {}
        _FurMap ("Fur Map", 2D) = "white" { }
        
        [Space(20)]
        _FurLength ("Fur Length", Range(0.0, 1)) = 0.5
        _FurDensity ("Fur Density", Range(0, 2)) = 0.11
        _FurThinness ("Fur Thinness", Range(0.01, 10)) = 1
        _FurShading ("Fur Shading", Range(0.0, 2)) = 0.25

        _GlobalForce ("Fur Global Force ", Vector) = (0, 0, 0, 0)
        _LocalForce ("Fur Local Force", Vector) = (0, 0, 0, 0)

         [Space(20)]
        _AnisotropicColor("Anisotropic Color", Color) = (1, 1, 1, 1)
        _Anisotropic ("Anisotropic", Range (0.03, 1)) = 0.078125
        [PowerSlider(5.0)] _Shininess ("Shininess", Range (0.1, 1)) = 0.078125

        [Space(20)]
        _FresnelColor("Fresnel Color", Color) = (1, 1, 1, 1)
        _FresnelScale("Fresnel Scale",Range(0,1)) =1
        _FresnelAttenuation("Fresnel Attenuation",Range(0,5)) =2.5
        
    }
    SubShader
    {
        Tags
        {
            "RenderPipeline" = "UniversalPipeline"
            "RenderType"="Opaque"
        }
        ZWrite On
        Cull Off
        Blend SrcAlpha OneMinusSrcAlpha
        
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

        TEXTURE2D(_BaseMap);    SAMPLER(sampler_BaseMap);
        TEXTURE2D(_FurMap);     SAMPLER(sampler_FurMap);
        half _FurOffset;

        CBUFFER_START(UnityPerMaterial)
            half4 _BaseMap_ST;
        
            half4 _BaseColor;

            half4 _FurMap_ST;
            half _FurLength;
            half _FurDensity;
            half _FurThinness;
            half _FurShading;

            half4 _GlobalForce;
            half4 _LocalForce;
        
            half4 _AnisotropicColor;
            half _Anisotropic;
            half _Shininess;
        
            half4 _FresnelColor;
            half _FresnelScale;
            half _FresnelAttenuation;
        CBUFFER_END

        struct a2v
        {
            float4 positionOS   : POSITION;
            float3 normalOS     : NORMAL;
            float4 tangentOS    : TANGENT;
            float2 texcoord     : TEXCOORD;
        };

        struct v2f
        {
            float4 positionCS   : SV_POSITION;
            float3 positionWS   : TEXCOORD0;
            float3 normalWS     : NORMAL;
            float3 tangentWS    : TANGENT;
            float3 bitangentWS  : TEXCOORD1;
            float4 uv           : TEXCOORD2;
        };

        half WardAnisotropicNormalDistribution(half anisotropic, half glossiness, half NdotL, half NdotV, half NdotH, half HdotX, half HdotY)
        {
            half aspect = sqrt(1.0h - anisotropic * 0.9h);

            half shininessPow2 = pow(1.0 - glossiness, 2);
            half X = max(.001, shininessPow2 / aspect) * 5;
            half Y = max(.001, shininessPow2 * aspect) * 5;
            half exponent = -(pow(HdotX / X, 2) + pow(HdotY / Y, 2)) / pow(NdotH, 2);
            half distribution = saturate(1.0 / (3.14159265 * X * Y * sqrt(NdotL * NdotV)));
            distribution *= exp(exponent);

            return distribution;
        }

        v2f vert(a2v input)
        {
            v2f output = (v2f)0;
            output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
            output.positionWS = TransformObjectToWorld(input.positionOS.xyz);
            output.normalWS = TransformObjectToWorldNormal(input.normalOS.xyz);
            output.uv.xy = TRANSFORM_TEX(input.texcoord, _BaseMap);
            return output;
        }

        
        half4 frag(v2f input):SV_Target
        {
            Light light = GetMainLight();
            half3 normalWS = normalize(input.normalWS);
            half3 ambient = SampleSH(normalWS);

            half4 fianlCol = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv.xy) * _BaseColor;

            half NdotL = saturate(dot(normalWS, light.direction));
            fianlCol.rgb *= NdotL * light.color + ambient;

            return half4(fianlCol.rgb, 1);
        }

        
        v2f vertLayer(a2v input)
        {
            v2f output;

            _FurLength *= 0.1;
            half mask = SAMPLE_TEXTURE2D_LOD(_BaseMap, sampler_BaseMap, input.texcoord, 0).a;
            float3 posOS = input.positionOS.xyz + input.normalOS.xyz * _FurOffset * _FurLength * mask;
            posOS += clamp(mul(unity_WorldToObject, _GlobalForce).xyz + _LocalForce.xyz, -1, 1) * pow(_FurOffset, 3) * _FurLength * mask;

            output.positionCS = TransformObjectToHClip(posOS);
            output.positionWS = TransformObjectToWorld(posOS);

            VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
            output.normalWS = normalInput.normalWS;
            output.tangentWS = normalInput.tangentWS;
            output.bitangentWS = normalInput.bitangentWS;

            output.uv.xy = TRANSFORM_TEX(input.texcoord, _BaseMap);
            output.uv.zw = TRANSFORM_TEX(input.texcoord, _FurMap) * _FurThinness;

            return output;
        }

        
        half4 fragLayer(v2f input):SV_Target
        {
            Light light = GetMainLight();
            half3 normalWS = normalize(input.normalWS);
            half3 viewDir = normalize(GetCameraPositionWS() - input.positionWS);
            half3 halfDir = normalize(light.direction + viewDir);
            half3 ambient = SampleSH(normalWS);

            half4 fianlCol = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv.xy) * _BaseColor;

            half shadingValue = (pow(1 - _FurOffset, 3)) * _FurShading;
            fianlCol.rgb = pow(abs(fianlCol.rgb), 1 + shadingValue);

            half furMask = SAMPLE_TEXTURE2D(_FurMap, sampler_FurMap, input.uv.zw).r;
            furMask = LinearToSRGB(furMask);

            half NdotL = saturate(dot(normalWS, light.direction));
            half NdotH = saturate(dot(normalWS, halfDir));
            half NdotV = saturate(dot(normalWS, viewDir));

            half HdotX = (dot(halfDir, input.tangentWS));
            half HdotY = (dot(halfDir, input.bitangentWS));

            half fresnel = pow(abs(1 - NdotV), _FresnelAttenuation) * _FresnelScale;
            half anisotropic = WardAnisotropicNormalDistribution(_Anisotropic, _Shininess, NdotL, NdotV, NdotH, HdotX, HdotY);

            fianlCol.rgb *= NdotL * light.color + ambient;
            fianlCol.rgb += anisotropic * furMask * fianlCol.a * _AnisotropicColor.rgb;
            fianlCol.rgb += _FresnelColor.rgb * fresnel;

            half alpha = clamp(furMask - pow(_FurOffset, 3) * _FurDensity, 0, 1) * fianlCol.a;

            return half4(fianlCol.rgb, alpha);
        }
        
        ENDHLSL
        
        pass
        {
            Tags
            {
                 "LightMode" = "FurRenderer"
            }
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
           ENDHLSL
        }

        pass
        {
            Tags
            {
                 "LightMode" = "FurRendererTransparent"
            }
            HLSLPROGRAM
            #pragma vertex vertLayer
            #pragma fragment fragLayer
           ENDHLSL
        }
    }
}